﻿// SMIL is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warrenty – Use at your own risk
// View the project and the latest code at http://smil.codeplex.com/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Windows.Controls.Primitives;
using GeoAPI;
using GisSharpBlog.NetTopologySuite;
using Microsoft.Maps.MapControl;

using DeepEarth.BingMapsToolkit.Client.MapGeometry;
using DeepEarth.BingMapsToolkit.Client.Common;
using DeepEarth.BingMapsToolkit.Client.Common.Entities;
using DeepEarth.BingMapsToolkit.Client.Controls;
using GeoAPI.Geometries;
using System.Windows.Browser;
using SMIL.Geometry;
using SMIL.DE.Extensions;
using SMIL.ApplicationVariables;

namespace SMIL.DrawingTools
{
    public class DrawingTool : Control, IDisposable
    {
        const string PART_tbPoint = "PART_tbPoint";
        const string PART_tbLine = "PART_tbLine";
        const string PART_tbPolygon = "PART_tbPolygon";
        const string PART_tbSelected = "PART_tbSelected";
        const string PART_drawingComboBox = "PART_drawingComboBox";
        const string PART_cbICurrentItem = "PART_cbICurrentItem";
        const string PART_drawingImage = "PART_drawingImage";
        const string PART_bSave = "PART_bSave";
        const string PART_bColor = "PART_bColor";
        const string PART_colorPicker = "PART_colorPicker";
        const string PART_geometryStylePicker = "PART_geometryStylePicker";
        const string PART_bDelete = "PART_bDelete";
        const string PART_bSaveDrawing = "PART_bSaveDrawing";
        const string PART_tbFreeDraw = "PART_tbFreeDraw";

        ToggleButton tbPoint;
        ToggleButton tbLine;
        ToggleButton tbPolygon;
        ToggleButton tbFreeDraw;
        ToggleButton tbSelected;
        ToggleButton bDelete;
        ComboBox cbDrawingBox;
        Image drawingImage;
        ComboBoxItem cbICurrentItem;
        Button bSave;
        Button bColor;
        Button bSaveDrawing;
        ColorPicker colorPicker;
        GeometryStylePicker geometryStylePicker;
        FreeDraw note;

        DrawingStates drawingState = DrawingStates.Normal;

        EditGeometry currentGeometry;
        IGeometry originalGeometry;
        int selectGeometries = 0;

        public DrawingTool()
        {
            IsEnabled = false;
            this.DefaultStyleKey = typeof(DrawingTool);

            Loaded += DrawingTool_Loaded;
        }

        void DrawingTool_Loaded(object sender, RoutedEventArgs e)
        {
            this.KeyUp += DrawingTool_KeyUp;
            SMIL.Geometry.StyledGeometry.SelectedSelectGeometry += styledGeom_SelectedSelectGeometry;
        }

        void styledGeom_SelectedSelectGeometry(object sender, EventArgs args)
        {
            selectGeom_Selected(sender, args);
        }

        void DrawingTool_KeyUp(object sender, KeyEventArgs e)
        {
            if (drawingState == DrawingStates.Delete && e.Key == Key.Escape)
            {
                bDelete_Click(sender, null);
            }

            else if (e.Key == Key.Escape)
            {
                ToggleMapStateAndCursor();
            }

        }

        #region ParentMap
        public static readonly DependencyProperty MapDepencyProperty = DependencyProperty.Register(
            "ParentMap", typeof(Map), typeof(DrawingTool), null);

        public Map ParentMap
        {
            get { return (Map)GetValue(MapDepencyProperty); }
            set
            {
                SetValue(MapDepencyProperty, (Map)value);
            }
        }

        #endregion

        public EnhancedMapLayer ScratchLayer { get; set; }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            tbPoint = (ToggleButton)GetTemplateChild(PART_tbPoint);
            tbLine = (ToggleButton)GetTemplateChild(PART_tbLine);
            tbPolygon = (ToggleButton)GetTemplateChild(PART_tbPolygon);
            tbFreeDraw = (ToggleButton)GetTemplateChild(PART_tbFreeDraw);
            tbSelected = (ToggleButton)GetTemplateChild(PART_tbSelected);

            cbDrawingBox = (ComboBox)GetTemplateChild(PART_drawingComboBox);
            cbICurrentItem = (ComboBoxItem)GetTemplateChild(PART_cbICurrentItem);
            drawingImage = (Image)GetTemplateChild(PART_drawingImage);
            bSave = (Button)GetTemplateChild(PART_bSave);
            bColor = (Button)GetTemplateChild(PART_bColor);
            colorPicker = (ColorPicker)GetTemplateChild(PART_colorPicker);
            geometryStylePicker = (GeometryStylePicker)GetTemplateChild(PART_geometryStylePicker);
            bDelete = (ToggleButton)GetTemplateChild(PART_bDelete);
            bSaveDrawing = (Button)GetTemplateChild(PART_bSaveDrawing);


            if (tbPoint != null)
                tbPoint.Checked += drawing_Checked;

            if (tbPolygon != null)
                tbPolygon.Checked += drawing_Checked;

            if (tbLine != null)
                tbLine.Checked += drawing_Checked;

            if (tbFreeDraw != null)
            {
                tbFreeDraw.Checked += drawing_Checked;
                tbFreeDraw.Unchecked += drawing_Unchecked;
            }

            if (cbDrawingBox != null)
            {
                cbDrawingBox.DropDownClosed += cbDrawingBox_DropDownClosed;
                cbDrawingBox.DropDownOpened += cbDrawingBox_DropDownOpened;
            }

            //if (bSave != null)
            //    bSave.Click += bSave_Click;

            if (bSaveDrawing != null)
                bSaveDrawing.Click += bSaveDrawing_Click;

            if (bColor != null)
                bColor.Click += bColor_Click;

            if (geometryStylePicker != null)
            {
                geometryStylePicker.SelectedStyle = new StyleSpecification();
                geometryStylePicker.Save += geometryStylePicker_Save;
                geometryStylePicker.Cancel += geometryStylePicker_Cancel;
            }

            if (bDelete != null)
                bDelete.Click += bDelete_Click;

            IsEnabled = true;
        }

        void drawing_Unchecked(object sender, RoutedEventArgs e)
        {
            if (sender == tbFreeDraw)
            {
                drawingState = DrawingStates.Normal;
                tbFreeDraw.IsChecked = false;

                note.Dispose();
                note = null;
                SetPanOn();
            }
        }

        void bSaveDrawing_Click(object sender, RoutedEventArgs e)
        {
            Save();
        }

        private void CreateStyledGeometry(ref HashSet<StyledGeometry> styledGeoms)
        {
            foreach (var item in ScratchLayer.Children)
            {
                if (item.Visibility == System.Windows.Visibility.Visible && (item is EnhancedMapShapeBase || item is EnhancedMapPoint))
                {
                    IGeometry geom = null;
                    StyleSpecification styleSpec = null;
                    if (item is EnhancedMapPolygon) // polygon
                    {
                        EnhancedMapPolygon polygon = (EnhancedMapPolygon)item;

                        geom = new GisSharpBlog.NetTopologySuite.Geometries.LinearRing(
                            DeepEarth.BingMapsToolkit.Client.Common.Converters.CoordinateConvertor.LocationCollectionToCoordinates(polygon.Locations)
                            );

                        GisSharpBlog.NetTopologySuite.Geometries.Polygon gPolygon = new GisSharpBlog.NetTopologySuite.Geometries.Polygon((GisSharpBlog.NetTopologySuite.Geometries.LinearRing)geom);

                        geom = gPolygon;
                        styleSpec = polygon.GeometryStyle;
                    }
                    else if (item is EnhancedMapPolyline) // polyline
                    {
                        EnhancedMapPolyline polyline = (EnhancedMapPolyline)item;
                        geom = new GisSharpBlog.NetTopologySuite.Geometries.LineString(
                            DeepEarth.BingMapsToolkit.Client.Common.Converters.CoordinateConvertor.LocationCollectionToCoordinates(polyline.Locations)
                            );
                        styleSpec = polyline.GeometryStyle;
                    }

                    else // point
                    {

                        EnhancedMapPoint point = (EnhancedMapPoint)item;
                        geom = new GisSharpBlog.NetTopologySuite.Geometries.Point(
                            DeepEarth.BingMapsToolkit.Client.Common.Converters.CoordinateConvertor.Convert(point.Location)
                            );
                        styleSpec = point.GeometryStyle;

                    }

                    StyledGeometry styledGeom = new StyledGeometry(styleSpecification: styleSpec, geometry: geom);
                    styledGeoms.Add(styledGeom);
                }
            }
        }

        void bDelete_Click(object sender, RoutedEventArgs e)
        {
            ToggleMapStateAndCursor();
        }

        void geometryStylePicker_Cancel(object sender, EventArgs e)
        {
            ToggleStylePanel();
        }

        public IGeometry Geometry
        {
            get
            {
                if (null == currentGeometry)
                    return null;
                return currentGeometry.Geometry;
            }
            set
            {
                if (value != null)
                {
                    originalGeometry = (IGeometry)value.Clone();
                    currentGeometry = new EditGeometry
                    {
                        MapInstance = ParentMap,
                        Geometry = (IGeometry)value.Clone(),
                    };
                }
                else
                {
                    originalGeometry = null;
                    currentGeometry = new EditGeometry
                    {
                        MapInstance = ParentMap
                    };
                }

                if (null == StyleSpecificaiton)
                    return;

                currentGeometry.StyleSpecification = StyleSpecificaiton;
            }

        }

        public StyleSpecification StyleSpecificaiton { get; set; }

        #region ToogleStylePanel
        bool isColorButtonVisible = false;
        void bColor_Click(object sender, RoutedEventArgs e)
        {
            ToggleStylePanel();
        }

        void geometryStylePicker_Save(object sender, EventArgs e)
        {
            ToggleStylePanel();

            if (currentGeometry != null)
            {
                CreateSelectGeometryFromCurrentGeometry();
            }
        }


        #endregion

        void SaveCurrentGeometry()
        {
            if (null == currentGeometry)
                return;

            currentGeometry.EraseMode = false;

            ToggleButtons(null);

            if (!ParentMap.Children.Contains(ScratchLayer))
                ParentMap.Children.Add(ScratchLayer);

            ToggleSaveAndColorButton(bColor.IsEnabled, true);

            SelectGeometry selectGeometry = CreateSelectGeometryFromCurrentGeometry();
            Save(selectGeometry);
            //currentGeometry_EditCompleted(currentGeometry, new EditGeometryChangedEventArgs { Geometry = currentGeometry.Geometry });
            //Save();

        }

        void cbDrawingBox_DropDownOpened(object sender, EventArgs e)
        {
            cbICurrentItem.Visibility = System.Windows.Visibility.Collapsed;
        }

        void cbDrawingBox_DropDownClosed(object sender, EventArgs e)
        {
            cbICurrentItem.Visibility = Visibility.Visible;
        }

        void drawing_Checked(object sender, RoutedEventArgs e)
        {
            ToggleButtons((ToggleButton)sender);

            if (ParentMap != null)
            {
                CheckScratchLayer();
                if (null == currentGeometry)
                    currentGeometry = new EditGeometry { MapInstance = ParentMap };

                if (sender == tbPoint) //new Point
                {
                    SetComboBoxSelectedIcon(tbPoint);
                    drawingState = DrawingStates.Drawing;

                    currentGeometry.StyleSpecification = new StyleSpecification()
                        {
                            IconURL = string.Concat(
                              Application.Current.Resources[ApplicationVariables.ApplicationVariables.SPSite.ToString()] as string,
                                "/_layouts/images/SMIL/pin.png"),
                            IconScale = 2.0
                        };

                    currentGeometry.AddNew(GeometryType.Point);
                }
                else if (sender == tbLine) // new Polyline
                {
                    SetComboBoxSelectedIcon(tbLine);
                    drawingState = DrawingStates.Drawing;
                    currentGeometry.AddNew(GeometryType.LineString);
                }
                else if (sender == tbPolygon) // new Polygon
                {
                    SetComboBoxSelectedIcon(tbPolygon);
                    drawingState = DrawingStates.Drawing;
                    currentGeometry.AddNew(GeometryType.Polygon);
                }
                else if (sender == tbFreeDraw)
                {
                    //currentGeometry.AddNew(GeometryType.GeometryCollection);
                    
                    note = new FreeDraw(ParentMap, ScratchLayer);
                    note.OnNoteDone += NoteDoneEvent;
                    drawingState = DrawingStates.Drawing;
                    currentGeometry = null;
                }

                if (currentGeometry != null)
                    currentGeometry.EditCompleted += currentGeometry_EditCompleted;

                if (sender != tbPoint && currentGeometry != null)
                {
                    geometryStylePicker.SelectedStyle.IconURL = "/_layouts/images/SMIL/pin.png";
                    currentGeometry.StyleSpecification = geometryStylePicker.SelectedStyle;
                }

            }
        }

        public void NoteDoneEvent(object sender, EventArgs e)
        {
            StyledGeometry note = (StyledGeometry)sender;
            currentGeometry = new EditGeometry() { MapInstance = ParentMap, Geometry = note.Geometry };
            SaveCurrentGeometry();
            //note.RenderNote(ScratchLayer);
            //IGeometry geometry = note.Geometry;
            //Save();

            drawing_Checked(tbFreeDraw, null);
        }

        void SetPanOn()
        {
            ParentMap.MousePan += (s, ee) => { ee.Handled = false; };
        }

        SelectGeometry CreateSelectGeometryFromCurrentGeometry()
        {
            SelectGeometry selectGeom = currentGeometry.Geometry.ToSelectGeometry(currentGeometry.StyleSpecification.Clone(), ParentMap, ScratchLayer);

            selectGeom.Selected += selectGeom_Selected;

            currentGeometry.Dispose();
            currentGeometry = null;


            if (!bDelete.IsEnabled)
                bDelete.IsEnabled = true;

            drawingState = DrawingStates.Normal;
            return selectGeom;
            //selectGeometries++;
        }

        private void DeleteSelectedGeom(SelectGeometry selectedGeom)
        {
            selectedGeom.Visibility = System.Windows.Visibility.Collapsed;
            selectedGeom.Selected -= selectGeom_Selected;
            selectedGeom.Dispose();
            selectedGeom = null;

            selectGeometries--;

            if (selectGeometries == 0)
                bDelete.IsEnabled = false;

        }

        void Save(SelectGeometry selectGeometry = null)
        {
            if (null == ScratchLayer || null == ScratchLayer.Children || ScratchLayer.Children.Count == 0)
                return;

            HashSet<StyledGeometry> styledGeoms = new HashSet<StyledGeometry>();

            SelectGeometry selectGeom;
            if (currentGeometry != null)
            {
                selectGeom = CreateSelectGeometryFromCurrentGeometry();
                styledGeoms.Add(new StyledGeometry(null, selectGeom.StyleSpecification.Clone(), selectGeom.Geometry));
            }

            if (selectGeometry != null)
                styledGeoms.Add(new StyledGeometry(null, selectGeometry.StyleSpecification.Clone(), selectGeometry.Geometry));

            CreateStyledGeometry(ref styledGeoms);

            var xmlGeoms = CreateStyledGeoms(styledGeoms);

            string theGeom = xmlGeoms.ToString();

            HtmlPage.Window.CreateInstance("WriteGeomToSharePoint", new string[] { xmlGeoms.ToString() });
        }

        private static System.Text.StringBuilder CreateStyledGeoms(System.Collections.Generic.HashSet<StyledGeometry> styledGeoms)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            using (System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(sb))
            {
                StyledGeometry.GetStyledGeomsAsXML(styledGeoms, xmlWriter);
            }
            return sb;
        }

        void currentGeometry_EditCompleted(object sender, EditGeometryChangedEventArgs args)
        {
            SaveCurrentGeometry();
        }

        void selectGeom_Selected(object sender, EventArgs args)
        {
            SelectGeometry selectedGeom = (SelectGeometry)sender;
            //if the user deleting
            if (drawingState == DrawingStates.Delete)
            {
                DeleteSelectedGeom(selectedGeom);
                ToggleMapStateAndCursor();
                return;
            }

            if (currentGeometry != null)
                CreateSelectGeometryFromCurrentGeometry();

            currentGeometry = new EditGeometry
            {
                MapInstance = ParentMap,
                Geometry = (IGeometry)selectedGeom.Geometry.Clone(),
                StyleSpecification = selectedGeom.StyleSpecification,
                ItemID = selectedGeom.ItemID
            };

            // set style to current
            geometryStylePicker.SelectedStyle = currentGeometry.StyleSpecification;

            DeleteSelectedGeom(selectedGeom);

            ToggleSaveAndColorButton(true, true);
        }

        private void CheckScratchLayer()
        {
            if (null == ScratchLayer)
            {
                ScratchLayer = new EnhancedMapLayer(ParentMap)
                {
                    MaxZoomLevel = 21,
                    MinZoomLevel = 1,
                    ID = "ScratchLayer",
                    ZIndex = 0
                };
            }


        }

        private void SetComboBoxSelectedIcon(ToggleButton tb)
        {
            Image tbSelectedImage = (Image)tb.Content;
            drawingImage.Source = tbSelectedImage.Source;
        }

        void ToggleMapStateAndCursor()
        {
            if (drawingState == DrawingStates.Normal)
            {
                ParentMap.Cursor = Cursors.Eraser;
                drawingState = DrawingStates.Delete;
            }
            else
            {
                ParentMap.Cursor = Cursors.Arrow;
                drawingState = DrawingStates.Normal;

                if (bDelete.IsChecked ?? true)
                    bDelete.IsChecked = false;
            }
        }

        void ToggleButtons(ToggleButton tb)
        {
            if (tbLine != null && tb != tbLine && (tbLine.IsChecked ?? true))
                tbLine.IsChecked = false;

            if (tbPoint != null && tb != tbPoint && (tbPoint.IsChecked ?? true))
                tbPoint.IsChecked = false;

            if (tbPolygon != null && tb != tbPolygon && (tbPolygon.IsChecked ?? true))
                tbPolygon.IsChecked = false;

            cbDrawingBox.IsDropDownOpen = false;

        }

        private void ToggleStylePanel()
        {
            if (!isColorButtonVisible)
            {
                VisualStateManager.GoToState(this, "MyMoueClick", true);

                if (currentGeometry != null)
                    currentGeometry.StyleSpecification = geometryStylePicker.SelectedStyle;
            }

            else
            {
                VisualStateManager.GoToState(this, "Normal", true);
            }

            isColorButtonVisible = !isColorButtonVisible;
        }

        void ToggleSaveAndColorButton(bool? colorIsEnabled = null, bool? saveIsEnabled = null)
        {
            if (null == colorIsEnabled)
                bColor.IsEnabled = !bColor.IsEnabled;
            else
                bColor.IsEnabled = (bool)colorIsEnabled;

            if (null == saveIsEnabled)
                bSave.IsEnabled = !bSave.IsEnabled;
            else
                bSave.IsEnabled = (bool)saveIsEnabled;

        }

        #region IDisposable Members

        public void Dispose()
        {
            tbPoint.Checked -= drawing_Checked;
            tbPolygon.Checked -= drawing_Checked;
            tbLine.Checked -= drawing_Checked;
            cbDrawingBox.DropDownClosed -= cbDrawingBox_DropDownClosed;
            cbDrawingBox.DropDownOpened -= cbDrawingBox_DropDownOpened;
            Loaded -= DrawingTool_Loaded;
            //bSave.Click -= bSave_Click;
            bColor.Click -= bColor_Click;
            geometryStylePicker.Save -= geometryStylePicker_Save;
            geometryStylePicker.Cancel -= geometryStylePicker_Cancel;
        }

        #endregion
    }
}
